using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace MDAControlsFramework.Helpers {
	/// <summary>
	/// class for fast manipulating with bitmap
	/// from http://blogs.msdn.com/windowsmobile/archive/2008/04/15/faster-c.aspx#comments
	/// </summary>
	public class FastBitmap: ICloneable {
		private Bitmap _image;
		private BitmapData _bitmapData;
		private int _height;
		private int _width;
		private int _stride;
		private byte[] _rgbValues;
		bool _locked = false;

		public FastBitmap(int x, int y) {
			_width = x;
			_height = y;
			_image = new Bitmap(x, y);
		}

		public FastBitmap(Bitmap bitmap, bool createCopy) {
			_width = bitmap.Width;
			_height = bitmap.Height;
			if (createCopy == true)
				_image = new Bitmap(bitmap);
			else
				_image = bitmap;
		}

		public object Clone() {
			FastBitmap clone = this.MemberwiseClone() as FastBitmap;
			clone._image = _image.Clone() as Bitmap;
			clone._rgbValues = _rgbValues.Clone() as byte[];
			return clone;
		}

		public int Height {
			get {
				return this._height;
			}
		}

		public int Width {
			get {
				return this._width;
			}
		}

		public byte[] GetAllPixels() {
			return _rgbValues;
		}

		public void SetAllPixels(byte[] pixels) {
			_rgbValues = pixels;
		}

		public Color GetPixel(int x, int y) {
			int s = _stride;
			int blue = _rgbValues[(y * s + (x * 3))];
			int green = _rgbValues[(y * s + (x * 3)) + 1];
			int red = _rgbValues[(y * s + (x * 3)) + 2];
			return Color.FromArgb(red, green, blue);
		}

		public byte[] GetPixelValues(int x, int y) {
			int w = _width;
			int s = _stride;
			byte[] c = new byte[3];
			c[2] = _rgbValues[(y * s + (x * 3))];
			c[1] = _rgbValues[(y * s + (x * 3)) + 1];
			c[0] = _rgbValues[(y * s + (x * 3)) + 2];
			return c;
		}

		public void SetPixel(int x, int y, Color cIn) {
			int s = _stride;
			_rgbValues[(y * s + (x * 3))] = cIn.B;
			_rgbValues[(y * s + (x * 3)) + 1] = cIn.G;
			_rgbValues[(y * s + (x * 3)) + 2] = cIn.R;
		}

		public void SetPixel(int x, int y, byte r, byte g, byte b) {
			int w = _width;
			int s = _stride;
			_rgbValues[(y * s + (x * 3))] = b;
			_rgbValues[(y * s + (x * 3)) + 1] = g;
			_rgbValues[(y * s + (x * 3)) + 2] = r;
		}

		public static implicit operator Image(FastBitmap bmp) {
			return bmp._image;
		}

		public static implicit operator Bitmap(FastBitmap bmp) {
			return bmp._image;
		}

		public void LockBitmap() {
			LockBitmap(new Rectangle(0, 0, _width, _height));
		}

		private void LockBitmap(Rectangle area) {
			if (_locked)
				return;
			_locked = true;
			_bitmapData = _image.LockBits(area, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			_stride = _bitmapData.Stride;
			IntPtr ptr = _bitmapData.Scan0;
			int numBytes = _bitmapData.Stride * _height;
			_rgbValues = new byte[numBytes];
			Marshal.Copy(ptr, _rgbValues, 0, numBytes);
		}

		public void UnlockBitmap() {
			if (!_locked)
				return;
			_locked = false;
			Marshal.Copy(_rgbValues, 0, _bitmapData.Scan0, _bitmapData.Stride * _image.Height);
			_image.UnlockBits(_bitmapData);
		}
	}
}